#include "../../stdafx.h"
#include "render/render.h"
#include "render/surface.h"
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>

namespace Render
{

__forceinline ALLEGRO_BITMAP* AllegroBitmap(SurfacePtr pSurface)
{
	return (ALLEGRO_BITMAP*) pSurface;
}

__forceinline const ALLEGRO_COLOR& AllegroColour(const Colour& rgba)
{
	return *(ALLEGRO_COLOR*) &rgba;
}

__forceinline float AllegroPixel(int i)
{
	return (float) i + 0.5f;
}

void SetRenderTarget(SurfacePtr surface)
{
	al_set_target_bitmap(AllegroBitmap(surface));
}

void SetClipRect(const Rect* pRect)
{
	if (pRect)
		al_set_clipping_rectangle(pRect->x, pRect->y, pRect->w, pRect->h);
	else
		al_set_clipping_rectangle(0, 0, GetWidth((Surface_Raw*) al_get_target_bitmap()), GetHeight((Surface_Raw*) al_get_target_bitmap()));
}

void BeginDeferredBitmapDrawing()
{
	al_hold_bitmap_drawing(true);
}

void EndDeferredBitmapDrawing()
{
	al_hold_bitmap_drawing(false);
}

void Clear(const Colour& rgba)
{
	al_clear_to_color(AllegroColour(rgba));
}

void DrawLine(int x1, int y1, int x2, int y2, const Colour& rgba)
{
	al_draw_line(AllegroPixel(x1), AllegroPixel(y1), AllegroPixel(x2), AllegroPixel(y2), AllegroColour(rgba), 1.f);
}

void DrawHLine(int x1, int y1, int x2, const Colour& rgba)
{
	al_draw_line(x1, AllegroPixel(y1), x2, AllegroPixel(y1), AllegroColour(rgba), 1.f);
}

void DrawVLine(int x1, int y1, int y2, const Colour& rgba)
{
	al_draw_line(AllegroPixel(x1), y1, AllegroPixel(x1), y2, AllegroColour(rgba), 1.f);
}

void DrawRect(int x1, int y1, int x2, int y2, const Colour& rgba)
{
	al_draw_rectangle(AllegroPixel(x1), AllegroPixel(y1), AllegroPixel(x2), AllegroPixel(y2), AllegroColour(rgba), 1.f);
}

void DrawPixel(int x, int y, const Colour& rgba)
{
	al_draw_filled_rectangle(AllegroPixel(x) - 0.5f, AllegroPixel(y) - 0.5f, AllegroPixel(x) + 0.5f, AllegroPixel(y) + 0.5f, AllegroColour(rgba));
}

void FillRect(int x1, int y1, int x2, int y2, const Colour& rgba)
{
	al_draw_filled_rectangle(x1, y1, x2, y2, AllegroColour(rgba));
}

void FillCircle(int x, int y, int r, const Colour& rgba)
{
	al_draw_filled_circle(AllegroPixel(x), AllegroPixel(y), (float) r, AllegroColour(rgba));
}

void DrawCircle(int x, int y, int r, const Colour& rgba)
{
	al_draw_circle(AllegroPixel(x), AllegroPixel(y), (float) r, AllegroColour(rgba), 0.5f);
}

void DrawSprite(const SurfacePtr src, int x, int y)
{
	al_draw_bitmap(AllegroBitmap(src), x, y, 0);
}

void Blit(const SurfacePtr src, int x, int y, const Rect& srcRect)
{
	al_draw_bitmap_region(AllegroBitmap(src), 
							srcRect.x, srcRect.y, srcRect.w, srcRect.h,
							x, y,
							0);
}

void Blit(const SurfacePtr src, int x, int y, const Rect& srcRect, const Colour& rgba)
{
	al_draw_tinted_bitmap_region(AllegroBitmap(src), AllegroColour(rgba),
									srcRect.x, srcRect.y, srcRect.w, srcRect.h,
									x, y,
									0);
}

void RotoZoom(const SurfacePtr src, int x, int y, const Render::Rect& srcRect, int offsetX, int offsetY, float rotation, float zoom)
{
	al_draw_tinted_scaled_rotated_bitmap_region(AllegroBitmap(src),
												srcRect.x, srcRect.y, srcRect.w - 0.5f, srcRect.h - 0.5f,
												al_map_rgb(255, 255, 255),
												offsetX, offsetY, 
												x, y, 
												zoom, zoom, rotation, 0);
}

class VertexBuffer
{
public:
	const static int MAX_SIZE = 100000;

	VertexBuffer()
	{
		vertices.resize(MAX_SIZE);
		pBegin = vertices.data();
		pEnd = pBegin + vertices.size();
		pNext = pBegin;
	}

	void Clear()
	{	
		pNext = pBegin;
	}

	void PushBack()
	{
		Assert(pNext < pEnd);
		pNext++;
	}

	ALLEGRO_VERTEX& Next()
	{
		return *pNext;
	}

	bool Empty() const
	{
		return pBegin == pNext;
	}

	int Size() const
	{
		return pNext - pBegin;
	}

	const ALLEGRO_VERTEX* Data() const
	{
		return pBegin;
	}

private:
	std::vector<ALLEGRO_VERTEX> vertices;
	ALLEGRO_VERTEX* pBegin;
	ALLEGRO_VERTEX* pEnd;
	ALLEGRO_VERTEX* pNext;
};

VertexBuffer g_vertices;
ALLEGRO_PRIM_TYPE g_type;

void AddVertex(VertexBuffer& buffer, float x, float y, const Colour& rgba)
{
	ALLEGRO_VERTEX& vertex(buffer.Next());

	vertex.x = x + 0.5f;
	vertex.y = y + 0.5f;
	vertex.z = 0.f;
	vertex.u = 0.f;
	vertex.v = 0.f;
	vertex.color = AllegroColour(rgba);

	buffer.PushBack();
}

void DrawPrimitive(const VertexBuffer& vertices)
{
	if (!vertices.Empty())
		al_draw_prim(vertices.Data(), 0, 0, 0, vertices.Size(), g_type);
}

void BeginDeferredPolygonRendering()
{
	g_vertices.Clear();
	g_type = ALLEGRO_PRIM_TRIANGLE_LIST;
}

void BeginDeferredLineRendering()
{
	g_vertices.Clear();
	g_type = ALLEGRO_PRIM_LINE_LIST;
}

void BeginDeferredPointRendering()
{
	g_vertices.Clear();
	g_type = ALLEGRO_PRIM_POINT_LIST;
}

void EndDeferredRendering()
{
	DrawPrimitive(g_vertices);
}

void DrawLine_Deferred(int x1, int y1, int x2, int y2, const Colour& rgba)
{
	AddVertex(g_vertices, x1, y1, rgba);
	AddVertex(g_vertices, x2, y2, rgba);
}

void DrawRect_Deferred(int x1, int y1, int x2, int y2, const Colour& rgba)
{
	AddVertex(g_vertices, x1, y1, rgba);
	AddVertex(g_vertices, x2, y1, rgba);
	AddVertex(g_vertices, x2, y1, rgba);
	AddVertex(g_vertices, x2, y2, rgba);
	AddVertex(g_vertices, x2, y2, rgba);
	AddVertex(g_vertices, x1, y2, rgba);
	AddVertex(g_vertices, x1, y2, rgba);
	AddVertex(g_vertices, x1, y1, rgba);
}

void FillRect_Deferred(int x1, int y1, int x2, int y2, const Colour& rgba)
{
	AddVertex(g_vertices, x1, y1, rgba);
	AddVertex(g_vertices, x2, y1, rgba);
	AddVertex(g_vertices, x2, y2, rgba);

	AddVertex(g_vertices, x2, y2, rgba);
	AddVertex(g_vertices, x1, y2, rgba);
	AddVertex(g_vertices, x1, y1, rgba);
}

void FillPolygon_Deferred(const float x[], const float y[], const Colour rgba[], int count)
{
	for (int i = 1; i < count - 1; i++)
	{
		AddVertex(g_vertices, x[0], y[0], rgba[0]);
		AddVertex(g_vertices, x[i], y[i], rgba[i]);
		AddVertex(g_vertices, x[i + 1], y[i + 1], rgba[i + 1]);
	}
}

void FillPolygonCentred_Deferred(const float x[], const float y[], const Colour rgba[], int count)
{
	for (int i = 1; i < count - 1; i++)
	{
		AddVertex(g_vertices, x[0], y[0], rgba[0]);
		AddVertex(g_vertices, x[i], y[i], rgba[i]);
		AddVertex(g_vertices, x[i + 1], y[i + 1], rgba[i + 1]);
	}

	AddVertex(g_vertices, x[0], y[0], rgba[0]);
	AddVertex(g_vertices, x[count - 1], y[count - 1], rgba[count - 1]);
	AddVertex(g_vertices, x[1], y[1], rgba[1]);
}

void DrawPixel_Deferred(int x, int y, const Colour& rgba)
{
	AddVertex(g_vertices, x, y, rgba);
}

}
